use crate::{errors::CustomError, models::course::*};

use sqlx::postgres::PgPool;

pub async fn get_all_db(pool: &PgPool) -> Result<Vec<Course>, CustomError> {
  let rows = sqlx::query!(r#"select * from course"#)
    .fetch_all(pool)
    .await?;

  let courses = rows
    .iter()
    .map(|r| Course {
      id: r.id,
      teacher_id: r.teacher_id,
      name: r.name.clone(),
      time: r.time.unwrap(),
      description: r.description.clone(),
      language: r.language.clone(),
      price: r.price,
    })
    .collect::<Vec<Course>>();

  if courses.is_empty() {
    Err(CustomError::NotFound("Data not found".into()))
  } else {
    Ok(courses)
  }
}

pub async fn get_courses_for_teacher_db(
  pool: &PgPool,
  teacher_id: i32,
) -> Result<Vec<Course>, CustomError> {
  let rows = sqlx::query!(
    r#"
    select * from course where teacher_id = $1
    "#,
    teacher_id
  )
  .fetch_all(pool)
  .await
  .unwrap();

  let courses: Vec<Course> = rows
    .iter()
    .map(|r| Course {
      id: r.id,
      teacher_id: r.teacher_id,
      name: r.name.clone(),
      time: r.time.unwrap(),
      description: r.description.clone(),
      language: r.language.clone(),
      price: r.price,
    })
    .collect();

  match courses.len() {
    0 => Err(CustomError::NotFound("Data not found".into())),
    _ => Ok(courses),
  }
}

pub async fn get_course_details_db(
  pool: &PgPool,
  teacher_id: i32,
  course_id: i32,
) -> Result<Course, CustomError> {
  let row = sqlx::query!(
    r#"
    select * from course where teacher_id = $1 and id = $2
    "#,
    teacher_id,
    course_id
  )
  .fetch_one(pool)
  .await;

  if let Ok(r) = row {
    Ok(Course {
      id: r.id,
      teacher_id: r.teacher_id,
      name: r.name.clone(),
      time: r.time.unwrap(),
      description: r.description.clone(),
      language: r.language.clone(),
      price: r.price,
    })
  } else {
    Err(CustomError::NotFound("Id not found".into()))
  }
}

pub async fn post_new_course_db(
  pool: &PgPool,
  new_course: CreateCourse,
) -> Result<Course, CustomError> {
  let row = sqlx::query!(
    r#"
        insert into
            course (teacher_id, name, description, language, price)
        values ($1, $2, $3, $4, $5)
        returning id, teacher_id, name, time, description, language, price
    "#,
    new_course.teacher_id,
    new_course.name,
    new_course.description,
    new_course.language,
    new_course.price
  )
  .fetch_one(pool)
  .await?;

  Ok(Course {
    id: row.id,
    teacher_id: row.teacher_id,
    name: row.name.clone(),
    time: row.time.unwrap(),
    description: row.description.clone(),
    language: row.language.clone(),
    price: row.price,
  })
}

pub async fn delete_course_db(
  pool: &PgPool,
  teacher_id: i32,
  course_id: i32,
) -> Result<String, CustomError> {
  let course_row = sqlx::query!(
    r#"delete from course where teacher_id = $1 and id = $2"#,
    teacher_id,
    course_id
  )
  .execute(pool)
  .await?;

  Ok(format!("Deleted {:?} record.", course_row))
}

pub async fn update_course_details_db(
  pool: &PgPool,
  teacher_id: i32,
  course_id: i32,
  update_course: UpdateCourse,
) -> Result<Course, CustomError> {
  let current_course_row = sqlx::query!(
    r#"select * from course where teacher_id = $1 and id = $2"#,
    teacher_id,
    course_id
  )
  .fetch_one(pool)
  .await?;

  let name: String = if let Some(name) = update_course.name {
    name
  } else {
    current_course_row.name
  };

  let description: String = if let Some(description) = update_course.description
  {
    description
  } else {
    current_course_row.description.unwrap_or_default()
  };

  let language: String = if let Some(language) = update_course.language {
    language
  } else {
    current_course_row.language.unwrap_or_default()
  };

  let course_row = sqlx::query!(
    r#"
      update course
        set name = $1, description = $2, language = $3
      where teacher_id = $4 and id = $5
      returning id, teacher_id, name, time, description, language, price
    "#,
    name,
    description,
    language,
    teacher_id,
    course_id
  )
  .fetch_one(pool)
  .await?;

  Ok(Course {
    id: course_row.id,
    teacher_id: course_row.teacher_id,
    name: course_row.name.clone(),
    time: course_row.time.unwrap(),
    description: course_row.description.clone(),
    language: course_row.language.clone(),
    price: course_row.price,
  })
}
